﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using AutoMapper;
using MementoClient.Classes;
using MementoClient.Interfaces;
using MementoClientConnectorInterfaces;
using System.ComponentModel;
using MementoLibrary;
using MementoLibrary.DTOs;
using GalaSoft.MvvmLight.Messaging;

namespace MementoClient.ViewModels
{
    public class MainViewModel : INotifyPropertyChanged
    {
        #region Private Member Variables
        private IDataRepository _dataRepository;
        private IEnumerable<CategoryGroupDTO> _categoryGroups;
        private string _selectedProjectName;
        private Task _selectedTask;
        private WorkLogVM _selectedWorklog;
        private IEnumerable<Task> _tasksForUser;
        private IEnumerable<Task> _filteredTasksForUser;
        private WorkLogVM _workLog;
        private List<WorkLogVM> _worklogs = new List<WorkLogVM>();
        private bool _isDataLoaded = false;
        private bool _isFilteredTaskListVisible;
        private string _status = String.Empty;
        private string _searchText = String.Empty;
        private string _selectedTaskName = String.Empty;
        private DateTime _selectedDate = DateTime.Now;
        private decimal _minutes;
        private LimitedQueue<string> _messages;
        #endregion

        #region Private Methods
        private void SubmitCommandExecute(object a)
        {
            var innerWorklog = WorkLog;
            Action<WorklogSavedResult> callback = worklogSavedResult =>
            {
                WorkLog = null;
                if (innerWorklog.WorkLogId == 0)
                {
                    innerWorklog.WorkLogId = worklogSavedResult.WorklogId;
                    WorkLogs.Add(innerWorklog);
                    PropertyChange("SelectedTaskRecentWorklogs");
                    PropertyChange("SelectedTaskHasWorklogs");
                }
                else
                {
                    var originalWorklog =
                        WorkLogs.First(
                            w => w.WorkLogId == innerWorklog.WorkLogId);
                    Mapper.Map(innerWorklog, originalWorklog);
                }
            };
            var worklogDTO = Mapper.Map<WorkLogDTO>(innerWorklog);
            worklogDTO.CategoryIDs = innerWorklog.Categories.Select(c => c.CategoryId).ToList();
            new SubmitWorklogService().Submit(worklogDTO, callback);
        }

        private TaskDTO BuildTaskDTOFromTask(Task task)
        {
            var projectDTO = new ProjectDTO
            {
                Name = task.ProjectName,
                SourceSystem = task.ProjectID
            };
            var taskDTO = new TaskDTO
            {
                Name = task.TaskName,
                SourceSystem = task.TaskID,
                Project = projectDTO
            };
            return taskDTO;
        }

        private WorkLogDTO BuildWorkLogDTOFromWorkLogVM(WorkLogVM workLogVM)
        {
            var workLogDTO = new WorkLogDTO
            {
                Task = workLogVM.Task,
                Workdate = workLogVM.Workdate,
                Minutes = workLogVM.Minutes,
                WorkLogId = workLogVM.WorkLogId
            };

            return workLogDTO;
        }

        private List<WorkLogVM> BuildUpVMs(IEnumerable<WorkLogDTO> worklogs)
        {
            var newWorklogs = new List<WorkLogVM>();
            //var categories = CategoryGroups.SelectMany(cg => cg.Categories).ToDictionary(c => c.CategoryId);
            foreach (var worklog in worklogs)
            {
                var workLogVM = Mapper.Map<WorkLogVM>(worklog);
                //var updatedTaskForWorklog = TasksForUser.FirstOrDefault(w => w.TaskID == worklog.Task.SourceSystem);
                //if (updatedTaskForWorklog != null)
                //{
                //    workLogVM.Task = BuildTaskDTOFromTask(updatedTaskForWorklog);
                //    workLogVM.Categories = new ObservableCollection<CategoryDTO>(worklog.CategoryIDs.Select(c => categories[c]));
                    newWorklogs.Add(workLogVM);
                //}
            }
            return newWorklogs;
        }
        
        private void GetWorkLogs()
        {
            WorkLogs = BuildUpVMs(_dataRepository.GetWorkLogs());
        }

        private void PropertyChange(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion

        #region Public Properties
        public RelayCommand Submit { get; set; }
        public RelayCommand CreateNewWorklog { get; set; }
        public RelayCommand EditThisWorklog { get; set; }
        public RelayCommand SearchTextChange { get; set; }
        public RelayCommand SelectedDateChange { get; set; }
        public RelayCommand TaskSelected { get; set; }
        public RelayCommand SearchTextFocus { get; set; }
        public RelayCommand EscapeKeyPress { get; set; }

        public IEnumerable<Task> TasksForUser
        {
            get { return _tasksForUser; }
            set
            {
                _tasksForUser = value;
                PropertyChange("TasksForUser");
            }
        }

        public IEnumerable<Task> FilteredTasksForUser
        {
            get { return _filteredTasksForUser; }
            set
            {
                _filteredTasksForUser = value;
                PropertyChange("FilteredTasksForUser");
            }
        }

        public string SelectedProjectName
        {
            get { return _selectedProjectName; }
            set
            {
                _selectedProjectName = value;
                PropertyChange("SelectedProjectName");
                PropertyChange("TasksInSelectedProject");
            }
        }

        public string SelectedTaskName
        {
            get { return _selectedTaskName; }
            set
            {
                _selectedTaskName = value;
                PropertyChange("SelectedTaskName");
            }
        }

        public DateTime SelectedDate
        {
            get { return _selectedDate; }
            set
            {
                _selectedDate = value;
                PropertyChange("SelectedDate");
            }
        }

        public IEnumerable<CategoryGroupDTO> CategoryGroups
        {
            get
            {
                return _categoryGroups;
            }
            set
            {
                _categoryGroups = value;
                PropertyChange("CategoryGroups");
            }
        }

        public WorkLogVM SelectedWorklog
        {
            get { return _selectedWorklog; }
            set
            {
                _selectedWorklog = value;
                PropertyChange("SelectedWorklog");
            }
        }

        public Task SelectedTask
        {
            get { return _selectedTask; }
            set
            {
                _selectedTask = value;
                PropertyChange("SelectedTask");
                PropertyChange("SelectedTaskRecentWorklogs");
                PropertyChange("SelectedTaskHasWorklogs");
            }
        }

        public WorkLogVM WorkLog
        {
            get { return _workLog; }
            set
            {
                _workLog = value;
                PropertyChange("WorkLog");
                PropertyChange("HasActiveWorklog");
            }
        }

        public bool HasActiveWorklog
        {
            get { return WorkLog != null; }
        }

        public bool HasWorkLogs
        {
            get { return true; }
        }

        public bool IsFilteredTaskListVisible
        {
            get { return _isFilteredTaskListVisible; }
            set
            {
                _isFilteredTaskListVisible = value;
                PropertyChange("IsFilteredTaskListVisible");
            }
        }

        public List<WorkLogVM> WorkLogs
        {
            get { return _worklogs; }
            set
            {
                _worklogs = value;
                PropertyChange("Worklogs");
            }
        }

        public bool IsBusy { get; set; }

        public bool IsDataLoaded
        {
            get { return _isDataLoaded; }
            set
            {
                _isDataLoaded = value;
                PropertyChange("IsDataLoaded");
            }
        }

        public string SearchText
        {
            get { return _searchText; }
            set
            {
                _searchText = value;
                PropertyChange("SearchText");
            }
        }

        public decimal Minutes 
        {
            get{return _minutes;}
            set
            {
                _minutes = value;
                PropertyChange("Minutes");
            }
        }

        public LimitedQueue<string> Messages
        {
            get { return _messages; }
            set
            {
                _messages = value;
                PropertyChange("Messages");
            }
        }

        #endregion

        #region Constructors
        public MainViewModel(IDataRepository dataRepository)
        {   
            _selectedTask = null;
            _dataRepository = dataRepository;
            _searchText = "Find a task...";
            _messages = new LimitedQueue<string>(5);

            // Define command behaviours / publishers for this ViewModel
            Submit = new RelayCommand(SubmitCommandExecute);
            CreateNewWorklog = new RelayCommand(CreateNewWorklogExecute);
            EditThisWorklog = new RelayCommand(EditThisWorklogExecute, o => SelectedWorklog != null);
            SearchTextChange = new RelayCommand(SearchTextChangeExecute);
            SearchTextFocus = new RelayCommand(SearchTextFocusExecute);
            SelectedDateChange = new RelayCommand(o => Messenger.Default.Send<DateTime>(SelectedDate));
            TaskSelected = new RelayCommand(o => Messenger.Default.Send<Task>(SelectedTask));
            EscapeKeyPress = new RelayCommand(HideFilteredTaskList);

            // Define subscribers for this ViewModel
            Messenger.Default.Register<Task>(this, (task) => SearchText = task.TaskName);
            Messenger.Default.Register<Task>(this, (task) => GetWorkLogs());
            Messenger.Default.Register<Task>(this, (task) => HideFilteredTaskList(task));

            // Fetch initial data for the application
            Messages.Enqueue("Loading category groups");
            _dataRepository.GetCategoryGroups(groups =>
            {
                CategoryGroups = groups;
                IsDataLoaded = true;
                Messages.Enqueue("Category groups loaded");
            });

            Messages.Enqueue("Loading tasks");
            _tasksForUser = _dataRepository.GetTasksForUser(String.Empty);
            Messages.Enqueue("Tasks loaded");
            
            //dataRepository.GetClientData(DateTime.Now.AddDays(-7), DateTime.Now, _userName, container =>
            //{
            //    Status = "Fetching data...";
            //    CategoryGroups = container.CategoryGroups;
            //    TasksForUser = container.Tasks;
            //    Worklogs = BuildUpVMs(container.WorkLogs.ToList());
            //    SelectedProjectName = ProjectNames.FirstOrDefault();
            //    IsDataLoaded = true;
            //    Status = "Data loaded";
            //});
            
        }
        #endregion

        #region Public Methods

        public void SearchTextChangeExecute(object a)
        {
            FilteredTasksForUser = _tasksForUser.Where(t => t.TaskName.Contains(SearchText));
            IsFilteredTaskListVisible = true;
        }

        public void SearchTextFocusExecute(object a)
        {
            SearchText = String.Empty;
        }
        public void HideFilteredTaskList(object a)
        {
            IsFilteredTaskListVisible = false;
        }
        public void EditThisWorklogExecute(object a)
        {
            var workLog = Mapper.Map<WorkLogVM>(SelectedWorklog);
            SetActiveWorkLog(workLog);
        }

        public void CreateNewWorklogExecute(object a)
        {
            if (SelectedTask != null)
            {
                var taskDTO = BuildTaskDTOFromTask(_selectedTask);
                var workLog = new WorkLogVM { Task = taskDTO, Workdate = DateTime.UtcNow, Minutes = this.Minutes };
                var workLogDTO = BuildWorkLogDTOFromWorkLogVM(workLog);
                _dataRepository.SaveWorkLog(workLogDTO);
                SetActiveWorkLog(workLog);
            }
        }

        private void SetActiveWorkLog(WorkLogVM workLog)
        {
            workLog.CategoryGroups = CategoryGroups;
            workLog.Submit = Submit;
            WorkLog = workLog;
        }

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

    }
}
